<!DOCTYPE html>
<html lang="en"><head>
    <title>CZH-DEV BLOG</title>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="format-detection" content="telephone=no" />
    <meta name="theme-color" content="#000084" />
    <link rel="icon" href="https://czh-dev.gitee.io/czh-blog.gitee.io//favicon.ico">
    <link rel="canonical" href="https://czh-dev.gitee.io/czh-blog.gitee.io/">
    
    
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"></button>
            <a class="brand" href="https://czh-dev.gitee.io/czh-blog.gitee.io/">CZH-DEV BLOG</a>
            <div class="nav-collapse collapse">
                <ul class="nav">
                    
                    
                        
                            <li>
                                <a href="/czh-blog.gitee.io/about/">
                                    
                                    <span>About</span>
                                </a>
                            </li>
                        
                    
                        
                            <li>
                                <a href="/czh-blog.gitee.io/post/">
                                    
                                    <span>All posts</span>
                                </a>
                            </li>
                        
                    
                        
                            <li>
                                <a href="/czh-blog.gitee.io/ebook/">
                                    
                                    <span>Resource</span>
                                </a>
                            </li>
                        
                    
                </ul>
            </div>
        </div>
    </div>
</nav><div id="content" class="container">
<div style="display: flex;">
  <div class="row-fluid navmargin">
    <div class="page-header">
      <h1>SpringBoot自动装配 - Sat, Feb 11, 2023</h1>
    </div>
    <p class="lead"></p>
    <h1 id="spring-boot自动装配">Spring Boot自动装配</h1>
<p>使用SpringBoot之后，一个整合了SpringMVC的WEB工程开发，变的无比简单，那些繁杂的配置都消失</p>
<p>不见了，这是如何做到的？</p>
<p>一切魔力的开始，都是从我们的main函数来的，所以我们再次来看下启动类：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#a6e22e">@SpringBootApplication</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">DemoApplication</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">main</span><span style="color:#f92672">(</span>String<span style="color:#f92672">[]</span> args<span style="color:#f92672">)</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>    	SpringApplication<span style="color:#f92672">.</span><span style="color:#a6e22e">run</span><span style="color:#f92672">(</span>DemoApplication<span style="color:#f92672">.</span><span style="color:#a6e22e">class</span><span style="color:#f92672">,</span> args<span style="color:#f92672">);</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p>我们发现特别的地方有两个：</p>
<ul>
<li>
<p>注解：@SpringBootApplication</p>
</li>
<li>
<p>run方法：SpringApplication.run()</p>
</li>
</ul>
<p>我们分别来研究这两个部分。</p>
<p><strong>了解</strong><code>@SpringBootApplication</code></p>
<p><code>Ctrl + 鼠标左键</code>点击进入，查看源码👇：</p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211672.png" alt="image-20230103204642722"></p>
<p>​                                                 <!-- raw HTML omitted --> 图1 - @SpringBootApplication部分源码<!-- raw HTML omitted --></p>
<p><strong>这里重点的注解有3个：</strong></p>
<ul>
<li>
<p><code>@SpringBootConfiguration</code></p>
</li>
<li>
<p><code>@EnableAutoConfiguration</code></p>
</li>
<li>
<p><code>@ComponentScan</code></p>
</li>
</ul>
<hr>
<p><strong>@SpringBootConfiguration</strong>(源码👇)</p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211332.png" alt="image-20230103205731799"></p>
<p>​                                                    <!-- raw HTML omitted -->图2 - @SpringBootConfiguration源码<!-- raw HTML omitted --></p>
<p>通过这段我们可以看出，在这个注解上面，又有一个 <code>@Configuration</code> 注解。</p>
<blockquote>
<p><strong>翻译以上注释：</strong></p>
<p><em>指示类提供Spring Boot应用程序@Configuration。可以用作Spring的标准@Configuration注释的替代，以便可以自动找到配置（例如在测试中）。 应用程序应该只包含一个@SpringBootConfiguration，大多数惯用的Spring Boot应用程序都会从@SpringBootApplication继承它。</em></p>
<p><em>自： 1.4.0</em></p>
<p><em>作者： 菲利普·韦伯</em></p>
</blockquote>
<p><strong>通过阅读上面的注释我们知道：</strong></p>
<p><code>这个注解的作用就是声明当前类是一个配置类，然后Spring会自动扫描到添加了@Configuration 的类，并且读取其中的配置信息。而 @SpringBootConfiguration 是来声明当前类是SpringBoot应用的配置类，项目中只能有一个。所以一般我们无需自己添加。</code></p>
<hr>
<p><strong>@ComponentScan</strong></p>
<p>我们的<code>@SpringBootApplication</code>注解声明的类就是main函数所在的启动类，因此扫描的包是该类所在包及其子包。因此，<strong>一般启动类会放在一个比较前的包目录中。</strong></p>
<hr>
<p><strong>@EnableAutoConfiguration</strong></p>
<p><code>@EnableAutoConfiguration 的作用，告诉SpringBoot基于你所添加的依赖，去“猜测”你想要如何配置Spring。</code>比如我们引入了 spring-boot-starter-web ，而这个启动器中帮我们添加了 tomcat 、SpringMVC 的依赖。此时自动配置就知道你是要开发一个web应用，所以就帮你完成了web及SpringMVC的默认配置了！</p>
<p>SpringBoot内部对大量的第三方库或Spring内部库进行了默认配置，这些配置是否生效，取决于我们是否引入了对应库所需的依赖，如果有那么默认配置就会生效。</p>
<p>那么</p>
<ul>
<li>
<p>这些默认配置是在哪里定义的呢？</p>
</li>
<li>
<p>为何依赖引入就会触发配置呢？</p>
</li>
</ul>
<p>@EnableAutoConfiguration注解中导入了一个AutoConfigurationImportSelector配置类，该类中有个getCandidateConfigurations方法，方法的作用是委托SpringFactoriesLoader去读取jar包中的METAINF/spring.factories文件, 并加载里面配置的自动配置对象，包括: AOP / PropertyPlaceholder /FreeMarker / HttpMessageConverter / Jackson / DataSource /DataSourceTransactionManager /DispatcherServlet / WebMvc 等等。</p>
<p>其实在我们的项目中，已经自动引入了一个依赖：spring-boot-autoconfigure，上面说到的这些自动配置类, 都来自该包。</p>
<p>虽然spring.factories文件中定义了很多配置类, 但并不是所有配置类都会生效, 只有符合条件的是才会自动装配。</p>
<p>我们来看一个我们熟悉的，例如SpringMVC，查看mvc 的自动配置类：</p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211610.png" alt="image-20230103213122314"></p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211899.png" alt="image-20230103213206136"></p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211044.png" alt="image-20230103213234745"></p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211417.png" alt="image-20230103212524152"></p>
<p>​                                                              <!-- raw HTML omitted -->图3 - External Libraries目录<!-- raw HTML omitted --></p>
<p>如果找不到这个位置，可以使用快捷键<code>双击Shift</code>-&gt;<code>输入WebMvcAutoConfiguration</code>即可</p>
<p>打开<code>WebMvcAutoConfiguration</code>源码👇：</p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211579.png" alt="image-20230103213633726"></p>
<p>​                                              <!-- raw HTML omitted -->图4 - WebMvcAutoConfiguration部分源码<!-- raw HTML omitted --></p>
<p><strong>我们看到这个类上的4个注解：</strong></p>
<ul>
<li>
<p><code>@Configuration </code>：声明这个类是一个配置类</p>
</li>
<li>
<p><code>@ConditionalOnWebApplication(type = Type.SERVLET)</code></p>
<p>ConditionalOn，翻译就是在某个条件下，此处就是满足项目的类是Type.SERVLET类型，我们现在的项目就满足了, 就是一个web工程</p>
</li>
<li>
<p><code>@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,WebMvcConfigurer.class })</code></p>
<p>这里的条件是OnClass，也就是满足以下类存在：Servlet、DispatcherServlet、WebMvcConfigurer。这里就是判断你是否引入了SpringMVC相关依赖，引入依赖后该条件成立，当前类的配置才会生效！</p>
</li>
<li>
<p><code>@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)</code></p>
<p>这个条件与上面不同，OnMissingBean，是说环境中没有指定的Bean这个才生效。其实这就是自定义配置的入口，也就是说，如果我们自己配置了一个WebMVCConfigurationSupport的bean, 代表容器里已经存在该bean了，那么这个默认配置就会失效！</p>
</li>
</ul>
<p>接着，我们查看WebMvcAutoConfiguration该类中定义了什么：</p>
<p><strong>视图解析器：</strong></p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211770.png" alt="image-20230103214122471"></p>
<p>​                                               <!-- raw HTML omitted -->图5 - WebMvcAutoConfiguration部分源码<!-- raw HTML omitted --></p>
<p><code>WebMvcAutoConfiguration</code>中使用了<code>@AutoConfigureAfter</code>注解, 意为指定的类加载完了后，再加载本类</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#a6e22e">@AutoConfigureAfter</span><span style="color:#f92672">({</span> DispatcherServletAutoConfiguration<span style="color:#f92672">.</span><span style="color:#a6e22e">class</span><span style="color:#f92672">,</span> TaskExecutionAutoConfiguration<span style="color:#f92672">.</span><span style="color:#a6e22e">class</span><span style="color:#f92672">,</span>
</span></span><span style="display:flex;"><span>      ValidationAutoConfiguration<span style="color:#f92672">.</span><span style="color:#a6e22e">class</span> <span style="color:#f92672">})</span>
</span></span></code></pre></div><blockquote>
<p>以上👆代码段可在<code>图4 - WebMvcAutoConfiguration源码</code>中找到,</p>
<p>下面的👇<code>DispatcherServletAutoConfiguration</code>也可在该位置找到</p>
</blockquote>
<hr>
<p>而<code>DispatcherServletAutoConfiguration</code>中又做了很多事情, 比如配置了前端控制器</p>
<p><img src="https://czh-pic.oss-cn-guangzhou.aliyuncs.com/202302111211420.png" alt="image-20230103215404183"></p>
<p>​                                  <!-- raw HTML omitted -->图6 - DispatcherServletAutoConfiguration部分源码<!-- raw HTML omitted --></p>
<h2 id="总结">总结</h2>
<ol>
<li>
<p><strong>@SpringBootApplication注解内部是3大注解功能的集成</strong></p>
<ul>
<li>``@ComponentScan:<code> 开启组件扫描</code></li>
<li>``@SpringBootConfiguration: <code>作用等同于@Configuration注解,也是用于标记配置类</code></li>
<li>``@EnableAutoConfiguration: <code>内部导入AutoConfigurationImportSelector,该类中有个getCandidateConfigurations方法, 读取jar包中META-INF/spring.factories文件中配置类,  再根据条件进行加载和配置</code>, 比如: AOP / PropertyPlaceholder / FreeMarker /HttpMessageConverter / Jackson / DataSource / DataSourceTransactionManager /DispatcherServlet / WebMvc 等等</li>
</ul>
</li>
<li>
<p><strong>SpringApplication.run(..)的作用</strong></p>
<ul>
<li>启动SpringBoot应用</li>
<li>加载自定义的配置类,完成自动配置功能</li>
<li>把当前项目配置到嵌入的Tomcat服务器</li>
<li>启动嵌入的Tomcat服务器</li>
</ul>
</li>
</ol>
<hr>
<p><strong>简单来说</strong>Springboot 的自动装配原理：</p>
<p><code>我面试的时候背的就是这个</code>👇👇👇👇👇</p>
<p>在 SpringBoot 的<code>@SpringBootApplication </code>注解中有一个注解<code>@EnableAutoConfiguration</code> ，这个注解的功能就是实现自动装配的。他的作用主要是扫描导入每个 jar 中的 spring.factory 中的内容，然后把里面的内容读取到内存中，其实就是读取一堆的配置类。然后引入之后里面有些<code>@Codictional </code>的注解，根据特定的条件帮我们创建 bean 并注入，省去我们自己配置 bean 的过程，简化了开发步骤</p>

    <h4><a href="https://czh-dev.gitee.io/czh-blog.gitee.io/">Back to Home</a></h4>
  </div>

  <div class="span3 bs-docs-sidebar" style="position:fixed;right: 40px;top: 50px;">
    <h1>catalogue</h1>
    <ul class="nav nav-list bs-docs-sidenav">
      <div class="toc-div">
        <nav id="TableOfContents">
  <ul>
    <li><a href="#总结">总结</a></li>
  </ul>
</nav>
      </div>
    </ul>
  </div>

</div>
<script src="https://cdn.jsdelivr.net/npm/gumshoejs@5.1.2/dist/gumshoe.min.js"></script>
<script>
  var spy = new Gumshoe('#TableOfContents a', {
    nested: true,
    nestedClass: 'active'
  });
</script>
<style>
   
  #TableOfContents li,
  #TableOfContents ul {
    list-style-type: none;
  }

  #TableOfContents ul {
    padding-left: 0px;
  }

  #TableOfContents li>a {
    display: block;
    padding: 4px 20px;
    font-size: 95%;
    color: #000000;
  }

  #TableOfContents li>a:hover,
  #TableOfContents li>a:focus {
    padding-left: 19px;
    color: #3A6bA5;
    text-decoration: none;
    background-color: transparent;
    border-left: 1px solid #3A6bA5;
  }

  #TableOfContents li.active>a,
  #TableOfContents li.active>a:hover,
  #TableOfContents li.active>a:focus {
    padding-left: 18px;
    font-weight: bold;
    color: #3A6bA5;
    background-color: transparent;
    border-left: 2px solid #3A6bA5;
  }

   
  #TableOfContents li>ul {
    padding-bottom: 10px;
  }

  #TableOfContents li li>a {
    padding-top: 1px;
    padding-bottom: 1px;
    padding-left: 30px;
    font-size: 14px;
    font-weight: normal;
  }

  #TableOfContents li li>a:hover,
  #TableOfContents li li>a:focus {
    padding-left: 29px;
  }

  #TableOfContents li li.active>a,
  #TableOfContents li li.active>a:hover,
  #TableOfContents li li.active>a:focus {
    padding-left: 28px;
    font-weight: 500;
  }

  #TableOfContents .nav-link.active+ul {
    display: block;
  }

  #TableOfContents li>ul {
    display: none;
  }

  #TableOfContents li.active>ul {
    display: inherit;
  }

  .toc-div {
    position: -webkit-sticky;
     
    position: sticky;
     
    top: 20px;
  }
</style>


        </div><footer class="container">
    <hr class="soften">
    <p>
    <a href="https://space.bilibili.com/1799809923">Love eating fried pork ribs</a> | 

&copy; 
<a href="http://jmf-portfolio.netlify.com" target="_blank">
    JM Fergeau
</a>
<span id="thisyear">2023</span>

    | My site


        | Built on <a href="//gohugo.io" target="_blank">Hugo</a>

</p>
    <p class="text-center">
        <a href="https://facebook.com">Facebook</a> 
        <a href="https://twitter.com">Twitter</a> 
        <a href="https://linkedin.com">Linkedin</a> 
        <a href="https://github.com">GitHub</a> 
        <a href="https://gitlab.com">GitLab</a>
    </p>
</footer>

</body><link rel="stylesheet" href="/czh-blog.gitee.io/css/bootstrap.css">
<link rel="stylesheet" href="/czh-blog.gitee.io/css/bootstrap-responsive.css">
<link rel="stylesheet" href="/czh-blog.gitee.io/css/style.css">

<script src="/czh-blog.gitee.io/js/jquery.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-386.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-transition.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-alert.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-modal.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-dropdown.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-scrollspy.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-tab.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-tooltip.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-popover.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-button.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-collapse.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-carousel.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-typeahead.js"></script>
<script src="/czh-blog.gitee.io/js/bootstrap-affix.js"></script>
<script>
    _386 = { 
        fastLoad: false ,
        onePass: false , 
        speedFactor: 1 
    };

    
    function ThisYear() {
        document.getElementById('thisyear').innerHTML = new Date().getFullYear();
    };
</script>
</html>
